home *** CD-ROM | disk | FTP | other *** search
/ The Best of MacTutor - S…e Code for Volumes 1 to 5 / The Best of MacTutor - Source Code for Volume 1-5 (Wayzata Technology)(6031)(1990).bin / Source Code / #05 (Dec85-Jan86) / pascal / Mondrian & dissolve source 1-13 / dissbits.asm next >
Assembly Source File  |  1990-01-01  |  43KB  |  981 lines

  1. ;
  2. ;  procedure dissBits (srcB, destB: bitMap; srcR, dstR: rect); external;
  3. ;
  4. ; mike morton
  5. ; release: 15 september 1985
  6. ;
  7. ; this is the version 5.2. 
  8. ;
  9. ; differences from version 5 to 5.2 are:
  10. ;       bug fixed in the full-screen-width case (first bit done right)
  11. ;       magic-constant table is compacted, stored in bytes instead of longs
  12. ;       some small changes to save space, plus the table saves about 100 bytes
  13. ;       tested by a program on several million cases; this helped find...
  14. ;       yet more bugs in log2 routine fixed ("bitwidth" routine added)
  15. ; differences from version 4 are:
  16. ;       nasty bug fixed in copying small rectangles
  17. ;       new address for correspondence
  18. ;       slightly more detailed notes on calling from C
  19. ;       various miscellaneous comment changes
  20. ; differences from version 3 are:
  21. ;       bugs in the log2 routine fixed
  22. ;       general case sped up about five percent
  23. ;       certain cases (e.g., the full screen) sped up over fifty percent
  24. ;       the time to dissolve is not directly related to the size of the rect
  25. ; differences between version 2 and version 3 are:
  26. ;       documentation improved and neatened
  27. ;       log2 routine rewritten
  28. ;
  29. ; comments and suggestions are, of course, welcome.
  30. ;
  31. ; ******************************************************************************
  32. ; *                                           *
  33. ; *            copyright 1984, 1985 by michael s. morton                  *
  34. ; *         permission to publish and distribute granted to MacTutor           *             
  35. ; *    please see details below on using, copying and changing this source.    *
  36. ; *                                           *
  37. ; ******************************************************************************
  38. ;
  39. ; what this routine does:
  40. ; ----------------------
  41. ;
  42. ; dissBits is like copyBits: it moves one rect to another, in their respective
  43. ; bitMaps.  it doesn't implement the modes of copyBits, nor clipping to a
  44. ; region.  what it DOES do is copy the bits in a pseudo-random order, giving
  45. ; the appearance of "dissolving" from one image to another.  the dissolve is
  46. ; rapid: the entire screen will dissolve in under four seconds.     (note: smaller
  47. ; areas may be SLOWER to dissolve -- see below.)
  48. ;
  49. ; copyBits pays attention to the current clipping.  this routine doesn't.
  50. ;
  51. ; other likely differences from copyBits:
  52. ;     o     the rectangles must have the same extents (not necessarily the same
  53. ;     lrbt).     if they are not, the routine will return -- doing nothing!
  54. ;     no stretching copy is done as copyBits would.
  55. ;     o     the cursor is hidden during the dissolve, since drawing is done without
  56. ;     quickdraw calls.  the cursor reappears when the drawing is finished.
  57. ;     for an odd effect, change it not to hide the cursor; is this how bill
  58. ;     atkinson thought of the spray can in MacPaint?
  59. ;     o     copyBits may be smart enough to deal with overlapping areas of memory.
  60. ;     this routine certainly isn't.
  61. ;     o     because this routine is desperate for speed, it steals the A5 (globals)
  62. ;     register, uses it in the central loop, then restores it before
  63. ;     returning. if you have vertical retrace tasks which run during the
  64. ;     dissolve, they'll wake up with the bogus A5.  since the ROM pulls this
  65. ;     trick too, i feel this isn't a bug in MY code, but it IS more likely
  66. ;     to expose bugs in VBL tasks.  to correct the problem, have your task
  67. ;     load A5 from the low RAM location "currentA5" immediately upon starting
  68. ;     up, then restore its caller's A5 before returning.
  69. ;
  70. ; you should know a few implementation details which may help:
  71. ;     o     copying from a dark area (lots of 1 bits) is slower than from a light
  72. ;     area.  but just barely (a few per cent).
  73. ;     o     there is no way to use this to randomly invert a rectangle.  instead,
  74. ;     copyBits it elsewhere, invert it, and dissBits it back into place.
  75. ;     o     there is also no way to slow the dissolve of a small area.  to do this,
  76. ;     copy a large area in which the only difference is the area to change.
  77. ;     o     if you fade in a solid area, you're likely to see patterns, since the
  78. ;     random numbers are so cheesy.  don't do this; fade in nifty patterns
  79. ;     which will distract your viewers.
  80. ;     o very small areas (less than 2 pixels in either dimension) are actually
  81. ;       done with a call to the real copyBits routine, since the pseudo-random
  82. ;       sequence generator falls apart under those conditions.
  83. ;
  84. ; a close relative of this routine is "dissBytes", which (as you might guess)
  85. ; copies a byte at a time, which is really fast (the whole screen in .1 or .2
  86. ; seconds).  it works only for certain rectangles, though.
  87. ;
  88. ; sample calling code:
  89. ; -------------------
  90. ;
  91. ; this is an excerpt from how a prerelease of DarTerminal called this routine.
  92. ; note the clever use of "paintbehind".     this took about 3 seconds to dissolve
  93. ; onto the screen.
  94. ;
  95. ;var   rg:      rgnhandle;                (* window to copy into *)
  96. ;      aport:   grafptr;                (* port to draw into *)
  97. ;      bits:    bitmap;                    (* new bitmap for that port *)
  98. ;      r:       rect;                    (* rectangle to draw into *)
  99. ;      pat:     pattern;
  100. ;      text:    packed array[1..37] of char;
  101. ; ...
  102. ;   aport := grafptr(newptr(sizeof(grafport))); (* get a port *)
  103. ;   openport(aport);                    (* make it current *)
  104. ;
  105. ;   r := theport^.portbits.bounds;            (* start with whole screen *)
  106. ;   insetrect(r,100,100);                (* get window-size rect *)
  107. ;   (* note that the number of bytes per row must be even! *)
  108. ;   bits.rowbytes := (((r.right-r.left)+15) div 16) * 2; (* find bytes/row *)
  109. ;   bits.baseaddr := qdptr(newptr(bits.rowbytes*(r.bottom-r.top))); (* get space *)
  110. ;   bits.bounds := r;                    (* boundary rect for bitmap *)
  111. ;
  112. ;   setportbits(bits);                    (* make new bitmap current *)
  113. ;
  114. ;   eraserect(r);
  115. ;   textfont(london); textsize(18); textface([bold]);
  116. ;   text := 'DarTerminal version -1.9  August 1984';
  117. ;   textbox(@text,37,r,tejustcenter);
  118. ;
  119. ;   dissbits(bits,screenport^.portbits,r,r);    (* dissolve it in *)
  120. ;
  121. ;   repeat until getnextevent(mdownmask+keydownmask,anevent); (* let 'em gawk *)
  122. ;
  123. ;   rg := newrgn;                    (* get a region to clip with *)
  124. ;   rectrgn(rg,r);                    (* as a rectangle *)
  125. ;   paintbehind(windowpeek(frontwindow),rg);    (* update this area of screen *)
  126. ;
  127. ;   disposergn(rg);
  128. ;   disposptr(ptr(bits.baseaddr));
  129. ;   disposptr(ptr(aport));
  130. ;
  131. ;
  132. ; calling from languages other than pascal:
  133. ; ----------------------------------------
  134. ;
  135. ; this routine uses the standard Lisa Pascal calling sequence.  to convert it to
  136. ; most C compilers, you'll probably just have to delete this instruction from
  137. ; near the end of the main routine:
  138. ;       add.l #psize,SP                ; unstack parameters
  139. ;
  140. ; i'd be very interested in hearing about successful uses of this routine from
  141. ; other languages.
  142. ;
  143. ; speed of the dissolve:
  144. ; ---------------------
  145. ;
  146. ; you need to pay attention to this section only if: (a) you want the dissolve
  147. ; to run as fast as it can OR (b) you do dissolves of various sizes and want
  148. ; them to take proportionate lengths of time.
  149. ;
  150. ; there are 3 levels of speed; the fastest possible one is chosen for you:
  151. ; (1) an ordinary dissolve will work when moving from any bitmap to any bitmap,
  152. ;     including on the Lisa under MacWorks.  this will dissolve at about 49
  153. ;     microseconds per pixel.  a rectangle one-quarter the size of the screen
  154. ;     will dissolve in just over two seconds.  the speed per pixel will vary
  155. ;     slightly, and will be less if your rect extents are close to but less
  156. ;     than powers of 2.
  157. ; (2) the dissolve will speed up if both the source and destination bitmaps have
  158. ;     rowBytes fields which are powers of two.  if you're copying to the screen
  159. ;     on a mac, the rowBytes field already satisfies this.  so, make your source
  160. ;     bitmap the right width for a cheap speedup -- about 20% faster.
  161. ; (3) the fanciest level is intended for copying the whole screen.  it'll paint
  162. ;     it in about 3.4 seconds (19 microseconds per pixel).  actually, painting
  163. ;     any rectangle which is the full width of the screen will run at this
  164. ;     speed, for what that's worth.
  165. ;
  166. ; duplication and use of this routine:
  167. ; -----------------------------------
  168. ;
  169. ; this is freeware.  you're welcome to copy it and use it in programs.  you're
  170. ; welcome to modify it, as long as you leave everything up until this section
  171. ; unchanged.  i'd be very interested in seeing your changes, especially if you
  172. ; find ways to make the central loops faster.  you're also welcome to port it
  173. ; to other machines/languages; i'd appreciate hearing about efforts to do this.
  174. ;
  175. ; this is "freeware"; please pay me if you use it.  why?
  176. ;
  177. ;       o if you have problems using it, i'll help you debug it.
  178. ;       o i'll send you improved, debugged, faster versions.
  179. ;       o i'll tell you about other products.  this is the first thing i ever
  180. ;      wrote for the Mac; wouldn't you like to see what else i've produced?
  181. ;      send me some positive feedback!
  182. ;
  183. ; how much should you pay?  my suggestion is:
  184. ;       (cost of one copy of the program) * (log10 of number of copies sold)
  185. ; if the subroutine is an integral part of your program, double this amount.
  186. ; if it's a frill (e.g., you dissolve in your "About MacWhatever"), halve it.
  187. ;
  188. ; i find it hard to believe that any damages to you or anyone else could come
  189. ; from bugs in this routine.  but, alas, whether or not you pay me, i can't be
  190. ; liable in any way for any problems in it.
  191. ;
  192. ; send comments, contributions, criticisms, or whatever to:
  193. ;       mike morton
  194. ;       INFOCOM
  195. ;       125 CambridgePark Dr.
  196. ;       Cambridge, MA  02140
  197. ;
  198. ; if, for some reason, you only have a hard copy of this and would like a
  199. ; source on a diskette, please contact:
  200. ;       MacTutor
  201. ;    Source Code Disks
  202. ;       P.O. Box 846
  203. ;       Placentia, CA. 92670
  204. ;       (714) 579-7700
  205. ;
  206.  
  207. ;
  208. ; things to think about:
  209. ; ---------------------
  210. ;
  211. ; o  clean up the register usage (as if i'll ever actually get around to this)
  212. ; o  use a dynamically-built table to avoid the multiply instruction in the general case.
  213. ;    this may not be a great idea since not everyone can afford that much space.
  214. ; o  adapt the routine to do transfer modes.  especially pattern modes, with no source.
  215. ;    it'd run significantly faster doing a fill of just black or white!     and patXor
  216. ;    would be pretty cool too (see also the XOR idea below).
  217. ; o  consider a front-end which does clipping (just like copyBits) by:
  218. ;       - allocate yet another offscreen bitmap, whose bounds are the intersection of
  219. ;      the destination rect with the bounding box of the destination's clipping rgn.
  220. ;       - copy from the destination's bitmap into the temporary bitmap.
  221. ;       - do a copyBits from the source to the temp, with the requested masking region.
  222. ;       - dissolve from the temp to the destination, thus actually dissolving that whole
  223. ;      rect, but changing only the clipped stuff.
  224. ; o  a nifty way to speed up things would be to XOR the destination into the source [sic!].
  225. ;    then the main loop doesn't have to copy a bit; it just tests if the bit is ON in the
  226. ;    altered source bitmap and, if so, TOGGLES it in the destination.  (i.e., it does a
  227. ;    srcXor operation).     to repair the source bitmap, a third XOR from the destination to
  228. ;    the source should do it [any old-timer will recognize this triple XOR as the best
  229. ;    way to swap two registers.]  the trick is making the invisible XOR run so fast that
  230. ;    the time to do it is less than the savings in the visible part.  or perhaps the batch
  231. ;    XORs could be done in spare-time...
  232. ; o  implement a partial dissolve.  this would let alex animate star trek-style teleporting.
  233. ;    there are a lot of ways to do this.  the easiest might be to include a counter in the
  234. ;    loop ("a register!     my kingdom for a register!").  alternately, we could assume that
  235. ;    the list of starting and ending points could be limited, allowing a table-based system.
  236. ;    this is something worth looking into, but could be real difficult.
  237. ; o  look at rearranging instructions so CPU-intensive ones aren't grouped, allowing us to
  238. ;    sneak in between video cycles more often.
  239. ; o  add some real error-handling.  how should we do this?  return a status?  fault?  we
  240. ;    could also return info to our caller on which of the three loops got used, so they know
  241. ;    they're getting the speed they want.
  242. ; o  don't use the BITWIDTH routine to test for exact powers of two in MULCHK.  a number X]
  243. ;    is an exact power of two if (x & -x) equals x.
  244. ;
  245. ;
  246. ;
  247. ;       -- end of introduction; real stuff starts here --
  248. ;
  249. ;
  250. ;
  251. ; DISSBITS SUBROUTINE
  252. ; MDS ASSEMBLER VERSION
  253. ; Converted by David E. Smith for MacTutor.
  254. ;
  255. ;
  256.     xdef    dissBits
  257.     
  258.     Include QuickEqu.D    ; MDS toolbox equates and traps
  259.     Include SysEqu.D
  260.     Include ToolEqu.D
  261.     Include MacTraps.D
  262.  
  263.     MACRO .equ = equ|    ; convert Lisa assembler stuff to MDS Mac
  264.     MACRO _hidecurs = _HideCursor|
  265.     MACRO _showcurs = _ShowCursor|
  266.     
  267. ;
  268. ; definitions of the "ours" record: this structure, of which there are two copies in
  269. ; our stack frame, is a sort of bitmap:
  270. ;
  271.  
  272. oRows   .equ 0                    ; (word) number of last row (first is 0)
  273. oCols   .equ oRows+2                ; (word) number of last column (first is 0)
  274. oLbits  .equ oCols+2                ; (word) size of left margin within 1st byte
  275. oStride .equ oLbits+2                ; (word) stride in memory from row to row
  276. oBase   .equ oStride+2                ; (long) base address of bitmap
  277.  
  278. osize   .equ oBase+4                ; size, in bytes, of "ours" record
  279.  
  280. ;
  281. ; stack frame elements:
  282. ;
  283.  
  284. srcOurs .equ -osize                ; (osize) our view of source bits
  285. dstOurs .equ srcOurs-osize            ; (osize) our view of target bits
  286.  
  287. sflast  .equ dstOurs                ; relative address of last s.f. member
  288. sfsize  .equ -sflast                ; size of s.f. for LINK (must be EVEN!)
  289. ;
  290. ;       parameter offsets from the stack frame pointer, A6:
  291. ;       last parameter is above return address and old s.f.
  292. ;
  293.  
  294. dRptr   .equ 4+4                ; ^destination rectangle
  295. sRptr   .equ dRptr+4                ; ^source rectangle
  296. dBptr   .equ sRptr+4                ; ^destination bitMap
  297. sBptr   .equ dBptr+4                ; ^source bitMap
  298.  
  299. plast   .equ sBptr+4                ; address just past last parameter
  300.  
  301. psize   .equ plast-dRptr            ; size of parameters, in bytes
  302.  
  303. ;
  304. ; entrance: set up a stack frame, save some registers, hide the cursor.
  305. ;
  306.  
  307. dissBits:                ; main entry point
  308.  
  309.         link A6,#-sfsize            ; set up a stack frame
  310.         movem.l D3-D7/A2-A5,-(SP)       ; save registers compiler may need
  311.         _hidecurs                ; don't let the cursor show for now
  312.  
  313. ;
  314. ; convert the source and destination bitmaps and rectangles to a format we prefer.
  315. ; we won't look at these parameters after this.
  316. ;
  317.  
  318.         move.l sBptr(A6),A0            ; point to source bitMap
  319.         move.l sRptr(A6),A1            ; and source rectangle
  320.         lea srcOurs(A6),A2            ; and our source structure
  321.         bsr CONVERT                ; convert to our format
  322.  
  323.         move.l dBptr(A6),A0            ; point to destination bitMap
  324.         move.l dRptr(A6),A1            ; and rectangle
  325.         lea dstOurs(A6),A2            ; and our structure
  326.         bsr CONVERT                ; convert to our format
  327.  
  328. ;
  329. ; check that the rectangles match in size.
  330. ;
  331.         move.w srcOurs+oRows(A6),D0     ; pick up the number of rows
  332.         cmp.w dstOurs+oRows(A6),D0      ; same number of rows?
  333.         bne ERROR                ; nope -- bag it
  334.  
  335.         move.w srcOurs+oCols(A6),D0     ; check the number of columns
  336.         cmp.w dstOurs+oCols(A6),D0      ; same number of columns, too?
  337.         bne ERROR                ; that's a bozo no-no
  338.  
  339. ;
  340. ; figure the bit-width needed to span the columns, and the rows.
  341. ;
  342.  
  343.         move.w srcOurs+oCols(A6),D0     ; get count of columns
  344.         ext.l D0                ; make it a longword
  345.         bsr LOG2                ; figure bit-width
  346.         move.w D0,D1                ; set aside that result
  347.         beq SMALL                ; too small?  wimp out and do it with copyBits
  348.  
  349.         move.w srcOurs+oRows(A6),D0     ; get count of rows
  350.         ext.l D0                ; make it a longword
  351.         bsr LOG2                ; again, find the bit-width
  352.         tst.w D0                ; is the result zero?
  353.         beq SMALL                ; if so, our algorithm will screw up
  354.  
  355. ;
  356. ; set up various constants we'll need in the in the innermost loop
  357. ;
  358.  
  359.         move.l #1,D5                ; set up...
  360.         lsl.l D1,D5                ; ...the bit mask which is...
  361.         sub.l #1,D5                ; ...bit-width (cols) 1's
  362.  
  363.         add.w D1,D0                ; find total bit-width (rows plus columns)
  364.         lea TABLE,A0                ; point to the table of XOR masks
  365.         moveq #0,D3                ; clear out D3 before we fill the low byte
  366.         move.b 0(A0,D0),D3            ; grab the correct XOR mask in D3
  367.  
  368. ;
  369. ; the table is saved compactly, since none of the masks are wider than a byte.  we have to unpack it so
  370. ; the high-order bit of the D0-bit-wide field is on:
  371. ;
  372.  
  373. UNPACK: add.l D3,D3                ; shift left by one
  374.         bpl.s UNPACK                ; keep moving until the top bit that's on is aligned at the top end
  375.  
  376.         rol.l D0,D3                ; now swing the top D0 bits around to be the bottom D0 bits, the mask
  377.         move.l D3,D0                ; 1st sequence element is the mask itself
  378.  
  379. ;
  380. ; do all kinds of preparation:
  381. ;
  382.  
  383.         move.l srcOurs+oBase(A6),D2     ; set up base pointer for our source bits
  384.         lsl.l #3,D2                ; make it into a bit address
  385.         move.l D2,A0                ; put it where the fast loop will use it
  386.         move.w srcOurs+oLbits(A6),D2    ; now pick up source left margin
  387.         ext.l D2                ; make it a longword
  388.         add.l D2,A0                ; and make A0 useful for odd routine below
  389.  
  390.         move.l dstOurs+oBase(A6),D2     ; set up base pointer for target
  391.         lsl.l #3,D2                ; again, bit addressing works out faster
  392.         move.l D2,A1                ; stuff it where we want it for the loop
  393.         move.w dstOurs+oLbits(A6),D2    ; now pick up destination left margin
  394.         ext.l D2                ; make it a longword
  395.         add.l D2,A1                ; and make A1 useful, too
  396.  
  397.         move.w srcOurs+oCols(A6),A2     ; pick up the often-used count of columns
  398.         move.w srcOurs+oRows(A6),D2     ; and of rows
  399.         add.w #1,D2                ; make row count one-too-high for compares
  400.         ext.l D2                ; and make it a longword
  401.         lsl.l D1,D2                ; slide it to line up w/rows part of D0
  402.         move.l D2,A4                ; and save that somewhere useful
  403.  
  404.         move.w D1,D2                ; put log2(columns) in a safe place (sigh)
  405.  
  406. ;
  407. ; try to reduce the amount we shift down D2.  this involves:
  408. ;    halving the strides as long as each is even, decrementing D2 as we go
  409. ;    masking the bottom bits off D4 when we extract the row count in the loop
  410. ;
  411. ; alas, we can't always shift as little as we want.  for instance, if we don't
  412. ; shift down far enough, the row count will be so high as to exceed a halfword,
  413. ; and the dread mulu instruction won't work (it eats only word operands).  so,
  414. ; we have to have an extra check to take us out of the loop early.
  415. ;
  416.  
  417.         move.w srcOurs+oStride(A6),D4   ; pick up source stride
  418.         move.w dstOurs+oStride(A6),D7   ; and target stride
  419.         move.w srcOurs+oRows(A6),D1     ; pick up row count for kludgey check
  420.  
  421.         tst.w D2                ; how's the bitcount?
  422.         beq.s HALFDONE                ; skip out if already down to zero
  423.  
  424. HALFLOOP:
  425.         btst #0,D4                ; is this stride even?
  426.         bne.s HALFDONE                ; nope -- our work here is done
  427.         btst #0,D7                ; how about this one?
  428.         bne.s HALFDONE                ; have to have both even
  429.  
  430.         lsl.w #1,D1                ; can we keep max row number in a halfword?
  431.         bcs.s HALFDONE                ; nope -- D2 mustn't get any smaller!
  432.  
  433.         lsr.w #1,D4                ; halve each stride...
  434.         lsr.w #1,D7                ; ...like this
  435.         sub.w #1,D2                ; and remember not to shift down as far
  436.         bne.s HALFLOOP                ; loop unless we're down to no shift at all
  437.  
  438. HALFDONE:                    ; no tacky platitudes, please
  439.         move.w D4,srcOurs+oStride(A6)   ; put back source stride
  440.         move.w D7,dstOurs+oStride(A6)   ; and target stride
  441.  
  442. ;
  443. ; make some stuff faster to access -- use the fact that (An) is faster to access
  444. ; than d(An).  this means we'll misuse our frame pointer, but don't worry -- we'll
  445. ; restore it before we use it again.
  446. ;
  447.  
  448.         move.w srcOurs+oStride(A6),A5   ; make source stride faster to access, too
  449.         move.l A6,-(SP)                ; save framitz pointer
  450.         move.w dstOurs+oStride(A6),A6   ; pick up destination stride
  451.         move.l #0,D6                ; we do only AND.W x,D6 -- but ADD.L D6,x
  452.  
  453.         clr.w -(SP)                ; reserve room for function result
  454.         bsr MULCHK                ; go see if strides are powers of two
  455.         tst.w (SP)+                ; can we eliminate the horrible MULUs?
  456.         bne  NOMUL                ; yes!  hurray!
  457.  
  458. ;
  459. ; main loop: map the sequence element into rows and columns, check if it's in bounds
  460. ; and skip on if it's not, flip the appropriate bit, generate the next element in the
  461. ; sequence, and loop if the sequence isn't done.
  462. ;
  463.  
  464. ;
  465. ; check the row bounds.     note that we can check the row before extracting it from
  466. ; D0, ignoring the bits at the bottom of D0 for the columns.  to get these bits
  467. ; to be ignored, we had to make A4 one-too-high before shifting it up to align it.
  468. ;
  469.  
  470. LOOP:                        ; here for another time around
  471.         cmp.l A4,D0                ; is row in bounds?
  472.         bge.s NEXT                ; no: clip this
  473.  
  474. ;
  475. ; map it into the column; check bounds.     note that we save this check for second;
  476. ; it's a little slower because of the move and mask.
  477. ;
  478. ; chuck sagely points out that when the "bhi" at the end of the loop takes, we
  479. ; know we can ignore the above comparison.  thanks, chuck.  you're a great guy.
  480. ;
  481.  
  482. LOOPROW:                ; here when we know the row number is OK
  483.         move.w D0,D6                ; copy the sequence element
  484.         and.w D5,D6                ; find just the column number
  485.  
  486.         cmp.w A2,D6                ; too far to the right? (past oCols?)
  487.         bgt.s NEXT                ; yes: skip out
  488.  
  489.         move.l D0,D4                ; we know element will be used; copy it
  490.         sub.w D6,D4                ; remove column's bits
  491.         lsr.l D2,D4                ; shift down to row, NOT right-justified
  492.  
  493. ;
  494. ; get the source byte, and bit offset.  D4 has the bit offset in rows, and
  495. ; D6 is columns.
  496. ;
  497.  
  498.         move.w A5,D1                ; get the stride per row (in bits)
  499.         mulu D4,D1                ; stride * row; find source row's offset in bits
  500.         add.l D6,D1                ; add in column offset (bits)
  501.         add.l A0,D1                ; plus base of bitmap (bits [sic])
  502.         move.b D1,D7                ; save the bottom three bits for the BTST
  503.         lsr.l #3,D1                ; while we shift down to a word address
  504.         move.l D1,A3                ; and save that for the test, too
  505.         not.b D7                ; get right bit number (compute #7-D7)
  506.  
  507. ;
  508. ; find the destination bit address and bit offset
  509. ;
  510.  
  511.         move.w A6,D1                ; extract cunningly hidden destination stride
  512.         mulu D1,D4                ; stride*row number = dest row's offset in bits
  513.         add.l D6,D4                ; add in column bit offset
  514.         add.l A1,D4                ; and base address, also in bits
  515.         move.b D4,D6                ; set aside the bit displacement
  516.         lsr.l #3,D4                ; make a byte displacement
  517.         not.b D6                ; get right bit number (compute #7-D6)
  518.  
  519.         btst D7,(A3)                ; test the D7th bit of source byte
  520.         move.l D4,A3                ; point to target byte (don't lose CC from btst)
  521.         bne.s SETON                ; if on, go set destination on
  522.         bclr D6,(A3)                ; else clear destination bit
  523.  
  524. ;
  525. ; find the next sequence element.  see knuth, vol ii., page 29 for sketchy details.
  526. ;
  527.  
  528. NEXT:                        ; jump here if D0 not in bounds
  529.         lsr.l #1,D0                ; slide one bit to the right
  530.         bhi.s LOOPROW                ; if no carry out, but not zero, loop
  531.  
  532.         eor.l D3,D0                ; flip magic bits appropriate to the bitwidth we want...
  533.         cmp.l D3,D0                ; ...but has this brought us to square 1?
  534.         bne.s LOOP                ; if not, loop back; else...
  535.  
  536.         bra DONE                ; ...we're finished
  537.  
  538. SETON:
  539.         bset D6,(A3)                ; source bit was on: set destination on
  540.  
  541.         ; copy of above code, stolen for inline speed -- sorry.
  542.         lsr.l #1,D0                ; slide one bit to the right
  543.         bhi.s LOOPROW                ; if no carry out, but not zero, loop
  544.         eor.l D3,D0                ; flip magic bits...
  545.         cmp.l D3,D0                ; ...but has this brought us to square 1?
  546.         bne.s LOOP                ; if not, loop back; else fall through
  547.  
  548.  
  549. ;
  550. ; here when we're done; the (0,0) point has not been done yet.  this is
  551. ; really the (0,left margin) point.  we also jump here from another copy loop.
  552. ;
  553.  
  554. DONE:
  555.         move.l (SP)+,A6                ; restore stack frame pointer
  556.  
  557.         move.w srcOurs+oLbits(A6),D0    ; pick up bit offset of left margin
  558.         move.w dstOurs+oLbits(A6),D1    ; and ditto for target
  559.         not.b D0                ; flip to number the bits for 68000
  560.         not.b D1                ; ditto
  561.  
  562. ; alternate, late entrance, when SCREEN routine has already set up D0 and D1 (it doesn't want the bit
  563. ; offset negated).
  564.  
  565. DONEA:                        ; land here with D0, D1 set
  566.         move.l srcOurs+oBase(A6),A0     ; set up base pointer for our source bits
  567.         move.l dstOurs+oBase(A6),A1     ; and pointer for target
  568.  
  569.         bset D1,(A1)                ; assume source bit was on; set target
  570.         btst D0,(A0)                ; was first bit of source on?
  571.         bne.s DONE2                ; yes: skip out
  572.         bclr D1,(A1)                ; no: oops!  set it right, and fall through
  573.  
  574. ;
  575. ; return
  576. ;
  577.  
  578. DONE2:                        ; here when we're really done
  579. ERROR:                        ; we return silently on errors
  580.         _showcurs                ; let's see this again
  581.  
  582.         movem.l (SP)+,D3-D7/A2-A5       ; restore lots of registers
  583.         unlk A6                    ; restore caller's stack frame pointer
  584.         move.l (SP)+,A0                ; pop return address
  585.         add.l #psize,SP                ; unstack parameters
  586.         jmp (A0)                ; home to mother
  587.  
  588. ;
  589. ; -----------------------------------------------------------------------------------
  590. ;
  591. ; sleazo code for when we're asked to dissolve very small regions.  if either dimension of the rectangle
  592. ; is too small, we bag it and just delegate the problem to copyBits.  a possible problem with this is if
  593. ; someone decides to substitute us for the standard copyBits routine -- this case will become recursive...
  594. ;
  595.  
  596. SMALL:                        ; here when it's too small to copy ourselves
  597.         move.l sBptr(A6),-(SP)            ; push args: source bitmap
  598.         move.l dBptr(A6),-(SP)            ;         destination bitmap
  599.         move.l sRptr(A6),-(SP)            ;         source rectangle
  600.         move.l dRptr(A6),-(SP)            ;         destination rectangle
  601.         move.w #srcCopy,-(SP)            ;         transfer mode -- source copy
  602.         clr.l -(SP)                ;         mask region -- NIL
  603.         _copyBits                ; do the copy in quickdraw-land
  604.         bra DONE2                ; head for home
  605.  
  606. ;
  607. ; -----------------------------------------------------------------------------------
  608. ;
  609. ; code identical to the usual loop, but A5 and A6 have been changed to shift counts.
  610. ; other than that, it's the same.  really it is!  well, no, wait a minute...
  611. ; because we don't have to worry about the word-size mulu operands, we can collapse
  612. ; the shifts and countershifts further as shown below:
  613.  
  614. NOMUL:                        ; here for alternate version of loop
  615.         tst.w D2                ; is right shift zero?
  616.         beq.s NOMUL2                ; yes: can't do much more...
  617.         cmp.w #0,A5                ; how about one left shift (for source stride)?
  618.         beq.s NOMUL2                ; yes: ditto
  619.         cmp.w #0,A6                ; and the other left shift (destination stride)?
  620.         beq.s NOMUL2                ; yes: can't do much more...
  621.  
  622.         sub.w #1,D2                ; all three...
  623.         sub.w #1,A5                ; ...are...
  624.         sub.w #1,A6                ; ...collapsible
  625.         bra.s NOMUL                ; go see if we can go further
  626.  
  627. ;
  628. ; see if we can do the super-special-case loop, which basically is equivalent to any rectangle
  629. ; where the source and destination are both exactly the width of the Mac screen.
  630. ;
  631.  
  632. NOMUL2:                        ; here when D2, A5, and A6 are all collapsed
  633.         tst.w D2                ; did this shift get down to zero?
  634.         bne.s NLOOP                ; no: skip to first kludged loop
  635.         cmp.w #0,A5                ; is this zero?
  636.         bne.s NLOOP                ; no: again, can't make further optimization
  637.         cmp.w #0,A6                ; how about this?
  638.         bne.s NLOOP                ; no: the best-laid plans of mice and men...
  639.         cmp.w A2,D5                ; is there no check on the column?
  640.         bne.s NLOOP                ; not a power-of-two columns; rats!
  641.  
  642.         move.w A0,D6                ; grab the base address of the source
  643.         and.b #7,D6                ; select the low three bits
  644.         bne.s NLOOP                ; doesn't sit on a byte boundary; phooey
  645.  
  646.         move.w A1,D6                ; now try the base of the destination
  647.         and.b #7,D6                ; and select its bit offset
  648.         beq.s SCREEN                ; yes!  do extra-special loop!
  649.  
  650. ;
  651. ; fast, but not super-fast loop, used when both source and destination bitmaps have strides which are
  652. ; powers of two.
  653. ;
  654.  
  655. NLOOP:                        ; here for another time around
  656.         cmp.l A4,D0                ; is row in bounds?
  657.         bge.s NNEXT                ; no: clip this
  658.  
  659. NLOOPROW:                    ; here when we know the row number is OK
  660.         move.w D0,D6                ; copy the sequence element
  661.         and.w D5,D6                ; find just the column number
  662.  
  663.         cmp.w A2,D6                ; too far to the right? (past oCols?)
  664.         bgt.s NNEXT                ; yes: skip out
  665.  
  666.         move.l D0,D4                ; we know element will be used; copy it
  667.         sub.w D6,D4                ; remove column's bits
  668.         lsr.l D2,D4                ; shift down to row, NOT right-justified
  669.  
  670.         move.w A5,D7                ; get log2 of stride per row (in bits)
  671.         move.l D4,D1                ; make a working copy of the row number
  672.         lsl.l D7,D1                ; * stride/row is source row's offset in bits
  673.         add.l D6,D1                ; add in column offset (bits)
  674.         add.l A0,D1                ; plus base of bitmap (bits [sic])
  675.         move.b D1,D7                ; save the bottom three bits for the BTST
  676.         lsr.l #3,D1                ; while we shift down to a byte address
  677.         move.l D1,A3                ; and save that for the test, too
  678.         not.b D7                ; get right bit number (compute #7-D7)
  679.  
  680.         move.w A6,D1                ; extract log2 of destination stride
  681.         lsl.l D1,D4                ; stride*row number = dest row's offset in bits
  682.         add.l D6,D4                ; add in column bit offset
  683.         add.l A1,D4                ; and base address, also in bits
  684.         move.b D4,D6                ; set aside the bit displacement
  685.         lsr.l #3,D4                ; make a byte displacement
  686.         not.b D6                ; get right bit number (compute #7-D6)
  687.  
  688.         btst D7,(A3)                ; test the D7th bit of source byte
  689.         move.l D4,A3                ; point to target byte (don't ruin CC from btst)
  690.         bne.s NSETON                ; if on, go set destination on
  691.         bclr D6,(A3)                ; else clear destination bit
  692.  
  693. NNEXT:                        ; jump here if D0 not in bounds
  694.         lsr.l #1,D0                ; slide one bit to the right
  695.         bhi.s NLOOPROW                ; if no carry out, but not zero, loop
  696.         eor.l D3,D0                ; flip magic bits...
  697.         cmp.l D3,D0                ; ...but has this brought us to square 1?
  698.         bne.s NLOOP                ; if not, loop back; else...
  699.         bra DONE                ; ...we're finished
  700.  
  701. NSETON:
  702.         bset D6,(A3)                ; source bit was on: set destination on
  703.         lsr.l #1,D0                ; slide one bit to the right
  704.         bhi.s NLOOPROW                ; if no carry out, but not zero, loop
  705.         eor.l D3,D0                ; flip magic bits...
  706.         cmp.l D3,D0                ; ...but has this brought us to square 1?
  707.         bne.s NLOOP                ; if not, loop back; else fall through
  708.         bra DONE                ; and finish
  709.  
  710. ;
  711. ; -----------------------------------------------------------------------------------
  712. ;
  713. ; super-special case, which happens to hold for the whole mac screen -- or subsets
  714. ; of it which are as wide as the screen. here, we've found that the shift counts
  715. ; in D2, A5, and A6 can all be collapsed to zero.  and D5 equals A2, so there's
  716. ; no need to check whether D6 is in limits -- or even take it out of D0!  so, this loop
  717. ; is the NLOOP code without the shifts or the check on the column number.  should
  718. ; run like a bat; have you ever seen a bat run?
  719. ;
  720. ; oh, yes, one further restriction -- the addresses in A0 and A1 must point to
  721. ; integral byte addresses with no bit offset.  (this still holds for full-screen
  722. ; copies.)  because both the source and destination are byte-aligned, we can skip
  723. ; the ritual Negation Of The Bit Offset which the 68000 usually demands.
  724.  
  725. SCREEN:                        ; here to set up to do the whole screen, or at least its width
  726.         move.l A0,D6                ; take the base source address...
  727.         lsr.l #3,D6                ; ... and make it a byte address
  728.         move.l D6,A0                ; replace pointer
  729.  
  730.         move.l A1,D6                ; now do the same...
  731.         lsr.l #3,D6                ; ...for...
  732.         move.l D6,A1                ; ...the destination address
  733.  
  734.         bra.s N2LOOP                ; jump into loop
  735.  
  736. N2HEAD:                        ; here when we shifted and a bit carried out
  737.         eor.l D3,D0                ; flip magic bits to make the sequence work
  738.  
  739. N2LOOP:                        ; here for another time around
  740.         cmp.l A4,D0                ; is row in bounds?
  741.         bge.s N2NEXT                ; no: clip this
  742.  
  743. N2LOOPROW:                    ; here when we know the row number is OK
  744.         move.l D0,D1                ; copy row number, shifted up, plus column offset
  745.         lsr.l #3,D1                ; while we shift down to a word offset
  746.  
  747.         btst D0,0(A0,D1)            ; test bit of source byte
  748.         bne.s N2SETON                ; if on, go set destination on
  749.         bclr D0,0(A1,D1)            ; else clear destination bit
  750.  
  751. N2NEXT:                        ; jump here if D0 not in bounds
  752.         lsr.l #1,D0                ; slide one bit to the right
  753.         bhi.s N2LOOPROW                ; if no carry out, but not zero, loop
  754.         bne.s N2HEAD                ; if carry out, but not zero, loop earlier
  755.         bra.s N2DONE                ; 0 means next sequence element would have been D3
  756.  
  757. N2SETON:
  758.         bset D0,0(A1,D1)            ; source bit was on: set destination on
  759.         lsr.l #1,D0                ; slide one bit to the right
  760.         bhi.s N2LOOPROW                ; if no carry out, but not zero, loop
  761.         bne.s N2HEAD                ; if carry out, but not zero, loop earlier
  762.                                         ; zero means the loop has closed on itself
  763.  
  764. ;
  765. ; because our bit-numbering isn't like that of the other two loops, we set up D0 and D1
  766. ; ourselves before joining a bit late with the common code to get the last bit.
  767. ;
  768.  
  769. N2DONE:
  770.         move.l (SP)+,A6                ; restore the stack frame pointer
  771.  
  772.         move.w srcOurs+oLbits(A6),D0    ; pick up bit offset of left margin
  773.         move.w dstOurs+oLbits(A6),D1    ; and ditto for target
  774.         bra DONEA                ; go do the first bit, which the sequence doesn't cover
  775.  
  776. ;
  777. ; -----------------------------------------------------------------------------------
  778. ;
  779.  
  780. ; mulchk -- see if we can do without multiply instructions.
  781. ;
  782. ; calling sequence:
  783. ;       A5 holds the source stride
  784. ;       A6 holds the destination stride
  785. ;       clr.w -(SP)                ; reserve room for boolean function return
  786. ;       bsr MULCHK                ; go check things out
  787. ;       tst.w (SP)+                ; test result
  788. ;       bne.s SHIFT                ; if non-zero, we can shift and not multiply
  789. ;
  790. ;       (if we can shift, A5 and A6 have been turned into shift counts)
  791. ;
  792. ; registers used: none  (A5, A6)
  793.  
  794. MULCHK:
  795.  
  796.         movem.l D0-D3,-(SP)            ; stack caller's registers
  797.         move.l A5,D0                ; take the source stride
  798.         bsr BITWIDTH                ; take log base 2
  799.         move.l #1,D1                ; pick up a one...
  800.         lsl.l D0,D1                ; ...and try to recreate the stride
  801.         cmp.l A5,D1                ; does it come out the same?
  802.         bne.s NOMULCHK                ; nope -- bag it
  803.         move.w D0,D3                ; save magic logarithm of source stride
  804.  
  805.         move.l A6,D0                ; yes -- now how about destination stride?
  806.         bsr BITWIDTH                ; convert that one, also
  807.         move.l #1,D1                ; again, try a single bit...
  808.         lsl.l D0,D1                ; ...and see if original # was 1 bit
  809.         cmp.l A6,D1                ; how'd it come out?
  810.         bne.s NOMULCHK                ; doesn't match -- bag this
  811.  
  812. ;
  813. ; we can shift instead of multiplying.  change address registers & tell our caller.
  814. ;
  815.         move.w D3,A5                ; set up shift for source stride
  816.         move.w D0,A6                ; and for destination stride
  817.         st 4+16(SP)                ; tell our caller what's what
  818.         bra.s MULRET                ; and return
  819.  
  820. NOMULCHK:
  821.  
  822.         sf 4+16(SP)                ; tell caller we can't optimize
  823. MULRET:                        ; here to return; result set
  824.         movem.l (SP)+,D0-D3            ; pop some registers
  825.         rts                    ; all set
  826.  
  827. ;
  828. ; -----------------------------------------------------------------------------------
  829. ;
  830. ; table of (longword) masks to XOR in strange Knuthian algorithm.  the first table
  831. ; entry is for a bit-width of two, so the table actually starts two bytes before
  832. ; that.     hardware jocks among you may recognize this scheme as the software analog
  833. ; of a "maximum-length sequence generator".
  834. ;
  835. ; to save a bit of room, masks are packed in bytes, but should be aligned as
  836. ; described in the code before being used.
  837. ;
  838.  
  839. .ALIGN 2
  840. table:  DC.B    0,0                    ; first element is #2
  841.     DC.B   3                    ; 2
  842.     DC.B   3                    ; 3
  843.     DC.B   3                    ; 4
  844.     DC.B   5                    ; 5
  845.     DC.B   3                    ; 6
  846.     DC.B   3                    ; 7
  847.     DC.B   23                    ; 8
  848.     DC.B   17                    ; 9
  849.     DC.B   9                    ; 10
  850.     DC.B   5                    ; 11
  851.     DC.B   101                    ; 12
  852.     DC.B   27                    ; 13
  853.     DC.B   53                    ; 14
  854.     DC.B   3                    ; 15
  855.     DC.B   45                    ; 16
  856.     DC.B   9                    ; 17
  857.     DC.B   129                    ; 18
  858.     DC.B   57                    ; 19
  859.     DC.B   9                    ; 20
  860.     DC.B   5                    ; 21
  861.     DC.B   3                    ; 22
  862.     DC.B   33                    ; 23
  863.     DC.B   27                    ; 24
  864.     DC.B   9                    ; 25
  865.     DC.B   113                    ; 26
  866.     DC.B   57                    ; 27
  867.     DC.B   9                    ; 28
  868.     DC.B   5                    ; 29
  869.     DC.B   101                    ; 30
  870.     DC.B   9                    ; 31
  871.     DC.B   163                    ; 32
  872. .align 2
  873.  
  874. ;
  875. ; -----------------------------------------------------------------------------------
  876. ;
  877. ; convert -- convert a parameter bitMap and rectangle to our internal form.
  878. ;
  879. ; calling sequence:
  880. ;       lea bitMap,A0                ; point to the bitmap
  881. ;       lea rect,A1                ; and the rectangle inside it
  882. ;       lea ours,A2                ; and our data structure
  883. ;       bsr CONVERT                ; call us
  884. ;
  885. ; when done, all fields of the "ours" structure are filled in:
  886. ;       oBase is the address of the first byte in which any bits are to be changed
  887. ;       oLbits is the number of bits into that first byte which are ignored
  888. ;       oStride is the stride from one row to the next, in bits
  889. ;       oCols is the number of columns in the rectangle
  890. ;       oRows is the number of rows
  891. ;
  892. ; registers used: D0, D1, D2
  893. ;
  894.  
  895. CONVERT:
  896.  
  897. ;
  898. ; save the starting word and bit address of the stuff:
  899. ;
  900.        move.w top(A1),D0            ; pick up top of inner rectangle
  901.        sub.w bounds+top(A0),D0            ; figure rows to skip within bitmap
  902.        mulu rowbytes(A0),D0            ; compute bytes to skip (relative offset)
  903.  
  904.        add.l baseaddr(A0),D0            ; find absolute address of first row to use
  905.  
  906.        move.w left(A1),D1            ; pick up left coordinate of inner rect
  907.        sub.w bounds+left(A0),D1            ; find columns to skip
  908.        move.w D1,D2                ; copy that
  909.        and.w #7,D2                ; compute bits to skip in first byte
  910.        move.w D2,oLbits(A2)            ; save that in the structure
  911.  
  912.        lsr.w #3,D1                ; convert column count from bits to bytes
  913.        ext.l D1                    ; convert to a long value, so we can...
  914.        add.l D1,D0                ; add to row start in bitmap to find 1st byte
  915.        move.l D0,oBase(A2)            ; save that in the structure
  916.  
  917. ;
  918. ; save the stride of the bitmap; this is the same as for the original, but in bits.
  919. ;
  920.        move.w rowbytes(A0),D0            ; pick up the stride
  921.        lsl.w #3,D0                ; multiply by eight to get a bit stride
  922.        move.w D0,oStride(A2)            ; stick it in the target structure
  923.  
  924. ;
  925. ; save the number of rows and columns.
  926. ;
  927.         move.w bottom(A1),D0            ; get the bottom of the rectangle
  928.         sub.w top(A1),D0            ; less the top coordinate
  929.         sub.w #1,D0                ; get number of highest row (1st is zero)
  930.         bmi.s CERROR                ; nothing to do?  (note: 0 IS ok)
  931.         move.w D0,oRows(A2);            ; save that in the structure
  932.  
  933.         move.w right(A1),D0            ; get the right edge of the rectangle
  934.         sub.w left(A1),D0            ; less the left coordinate
  935.         sub.w #1,D0                ; make it zero-based
  936.         bmi CERROR                ; nothing to do here?
  937.         move.w D0,oCols(A2)            ; save that in the structure
  938.  
  939. ;
  940. ; all done.  return.
  941. ;
  942.         rts
  943.  
  944. ;
  945. ; error found in CONVERT.  pop return and jump to the error routine, such as it is.
  946. ;
  947. CERROR:
  948.         addq.l #4,SP                ; pop four bytes of return address.
  949.         bra ERROR                ; return silently
  950.  
  951. ;
  952. ; -----------------------------------------------------------------------------------
  953. ;
  954. ; log2 -- find the ceiling of the log, base 2, of a number.
  955. ; bitwidth -- find how many bits wide a number is
  956. ;
  957. ; calling sequence:
  958. ;       move.l N,D0                ; store the number in D0
  959. ;       bsr LOG2                ; call us
  960. ;       move.w D0,...                ; D0 contains the word result
  961. ;
  962. ; registers used: D2, (D0)
  963. ;
  964.  
  965. BITWIDTH:
  966.         sub.l #1,D0                ; so 2**n works right (sigh)
  967. LOG2:
  968.         tst.l D0                ; did they pass us a zero?
  969.         beq.s LOGDONE                ; call log2(0) zero -- what the heck...
  970.         beq.s LOGDONE                ; if D0 was one, answer is zero
  971.         move.w #32,D2                ; initialize count
  972. LOG2LP:
  973.         lsl.l #1,D0                ; slide bits to the left by one
  974.         dbcs D2,LOG2LP                ; decrement and loop until a bit falls off
  975.  
  976.         move.w D2,D0                ; else save our value where we promised it
  977. LOGDONE:                        ; here with final value in D0
  978.         rts                    ; and return
  979.  
  980.     END                        ; procedure dissBits
  981.